/*
 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
 *
 * (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
 *                           Jerremy Koot (jkoot@snes9x.com)
 *
 * Super FX C emulator code 
 * (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
 *                           Gary Henderson.
 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
 *
 * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
 * C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_.
 * C4 C code (c) Copyright 2001 Gary Henderson (gary.henderson@ntlworld.com).
 *
 * DOS port code contains the works of other authors. See headers in
 * individual files.
 *
 * Snes9x homepage: http://www.snes9x.com
 *
 * Permission to use, copy, modify and distribute Snes9x in both binary and
 * source form, for non-commercial purposes, is hereby granted without fee,
 * providing that this license information and copyright notice appear with
 * all copies and any derived work.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event shall the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Snes9x is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Snes9x or software derived from Snes9x.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so everyone can benefit from the modifications
 * in future versions.
 *
 * Super NES and Super Nintendo Entertainment System are trademarks of
 * Nintendo Co., Limited and its subsidiary companies.
 */
#include "snes9x.h"
#include "dsp1.h"
#include "missing.h"
#include "memmap.h"
#include <math.h>

#include "dsp1emu.c"

void S9xInitDSP1 ()
{
    static bool8_32 init = FALSE;
    
    if (!init)
    {
        InitDSP ();
        init = TRUE;
    }
}

void S9xResetDSP1 ()
{
    S9xInitDSP1 ();
    
    DSP1.waiting4command = TRUE;
    DSP1.in_count = 0;
    DSP1.out_count = 0;
    DSP1.in_index = 0;
    DSP1.out_index = 0;
    DSP1.first_parameter = TRUE;
}

uint8 S9xGetDSP (uint16 address)
{
    uint8 t;

#ifdef DEBUGGER
    if (Settings.TraceDSP)
    {
        sprintf (String, "DSP read: 0x%04X", address);
        S9xMessage (S9X_TRACE, S9X_TRACE_DSP1, String);
    }
#endif
    if ((address & 0xf000) == 0x6000 ||
        (address >= 0x8000 && address < 0xc000))
    {
        if (DSP1.out_count)
        {
            if ((address & 1) == 0)
                t = (uint8) DSP1.output [DSP1.out_index];
            else
            {
                t = (uint8) (DSP1.output [DSP1.out_index] >> 8);
                DSP1.out_index++;
                if (--DSP1.out_count == 0)
                {
                    if (DSP1.command == 0x1a || DSP1.command == 0x0a)
                    {
                        DSPOp0A ();
                        DSP1.out_count = 4;
                        DSP1.out_index = 0;
                        DSP1.output [0] = Op0AA;
                        DSP1.output [1] = Op0AB;
                        DSP1.output [2] = Op0AC;
                        DSP1.output [3] = Op0AD;
                    }
                }
                DSP1.waiting4command = TRUE;
            }
        }
        else
        {
            // Top Gear 3000 requires this value....
        //              if(4==Settings.DSPVersion)
            t = 0xff;
                        //Ballz3d requires this one:
        //              else t = 0x00;
        }
    }
    else t = 0x80;

    return (t);
}

void S9xSetDSP (uint8 byte, uint16 address)
{
#ifdef DEBUGGER
    missing.unknowndsp_write = address;
    if (Settings.TraceDSP)
    {
        sprintf (String, "DSP write: 0x%04X=0x%02X", address, byte);
        S9xMessage (S9X_TRACE, S9X_TRACE_DSP1, String);
    }
#endif
    if ((address & 0xf000) == 0x6000 ||
        (address >= 0x8000 && address < 0xc000))
    {
        if ((address & 1) == 0)
        {
            if (DSP1.waiting4command)
            {
                DSP1.command = byte;
                DSP1.in_index = 0;
                DSP1.waiting4command = FALSE;
                DSP1.first_parameter = TRUE;

                // Mario Kart uses 0x00, 0x02, 0x06, 0x0c, 0x28, 0x0a
                switch (byte)
                {
                    case 0x00: DSP1.in_count = 2;       break;
                    case 0x10: DSP1.in_count = 2;       break;
                    case 0x04: DSP1.in_count = 2;       break;
                    case 0x08: DSP1.in_count = 3;       break;
                    case 0x18: DSP1.in_count = 4;       break;
                    case 0x28: DSP1.in_count = 3;       break;
                    case 0x0c: DSP1.in_count = 3;       break;
                    case 0x1c: DSP1.in_count = 6;       break;
                    case 0x02: DSP1.in_count = 7;       break;
                    case 0x0a: DSP1.in_count = 1;       break;
                    case 0x1a: DSP1.in_count = 1;       break;
                    case 0x06: DSP1.in_count = 3;       break;
                    case 0x0e: DSP1.in_count = 2;       break;
                    case 0x01: DSP1.in_count = 4;       break;
                    case 0x11: DSP1.in_count = 4;       break;
                    case 0x21: DSP1.in_count = 4;       break;
                    case 0x0d: DSP1.in_count = 3;       break;
                    case 0x1d: DSP1.in_count = 3;       break;
                    case 0x2d: DSP1.in_count = 3;       break;
                    case 0x03: DSP1.in_count = 3;       break;
                    case 0x13: DSP1.in_count = 3;       break;
                    case 0x23: DSP1.in_count = 3;       break;
                    case 0x0b: DSP1.in_count = 3;       break;
                    case 0x1b: DSP1.in_count = 3;       break;
                    case 0x2b: DSP1.in_count = 3;       break;
                    case 0x14: DSP1.in_count = 6;       break;
                                case 0x0f: DSP1.in_count = 1;   break;
//                  case 0x80: DSP1.in_count = 2;       break;
                    default:
                                        //printf("Op%02X\n",byte);
                    case 0x80:
                        DSP1.in_count = 0;
                        DSP1.waiting4command = TRUE;
                        DSP1.first_parameter = TRUE;
                        break;
                }
            }
            else
            {
                DSP1.parameters [DSP1.in_index] = byte;
                DSP1.first_parameter = FALSE;
            }
        }
        else
        {
            if (DSP1.waiting4command ||
                (DSP1.first_parameter && byte == 0x80))
            {
                DSP1.waiting4command = TRUE;
                DSP1.first_parameter = FALSE;
            }
                        else if (DSP1.first_parameter)
            {
            }
            else
            {
                if (DSP1.in_count)
                {
                    DSP1.parameters [DSP1.in_index] |= (byte << 8);
                    if (--DSP1.in_count == 0)
                    {
                        // Actually execute the command
                        DSP1.waiting4command = TRUE;
                        DSP1.out_index = 0;
                        switch (DSP1.command)
                        {
                            case 0x00:  // Multiple
                                Op00Multiplicand = (int16) DSP1.parameters [0];
                                Op00Multiplier = (int16) DSP1.parameters [1];

                                DSPOp00 ();

                                DSP1.out_count = 1;
                                DSP1.output [0] = Op00Result;
                                break;

                            case 0x10:  // Inverse
                                Op10Coefficient = (int16) DSP1.parameters [0];
                                Op10Exponent = (int16) DSP1.parameters [1];

                                DSPOp10 ();

                                DSP1.out_count = 2;
                                DSP1.output [0] = (uint16) (int16) Op10CoefficientR;
                                DSP1.output [1] = (uint16) (int16) Op10ExponentR;
                                break;

                            case 0x04:  // Sin and Cos of angle
                                Op04Angle = (int16) DSP1.parameters [0];
                                Op04Radius = (uint16) DSP1.parameters [1];

                                DSPOp04 ();

                                DSP1.out_count = 2;
                                DSP1.output [0] = (uint16) Op04Sin;
                                DSP1.output [1] = (uint16) Op04Cos;
                                break;

                            case 0x08:  // Radius
                                Op08X = (int16) DSP1.parameters [0];
                                Op08Y = (int16) DSP1.parameters [1];
                                Op08Z = (int16) DSP1.parameters [2];

                                DSPOp08 ();

                                DSP1.out_count = 2;
                                DSP1.output [0] = (int16) Op08Ll; 
                                DSP1.output [1] = (int16) Op08Lh;
                                break;

                            case 0x18:  // Range
                                
                                Op18X = (int16) DSP1.parameters [0];
                                Op18Y = (int16) DSP1.parameters [1];
                                Op18Z = (int16) DSP1.parameters [2];
                                Op18R = (int16) DSP1.parameters [3];

                                DSPOp18 ();

                                DSP1.out_count = 1;
                                DSP1.output [0] = Op18D;
                                break;

                            case 0x28:  // Distance (vector length)
                                Op28X = (int16) DSP1.parameters [0];
                                Op28Y = (int16) DSP1.parameters [1];
                                Op28Z = (int16) DSP1.parameters [2];

                                DSPOp28 ();

                                DSP1.out_count = 1;
                                DSP1.output [0] = (uint16) Op28R;
                                break;

                            case 0x0c:  // Rotate (2D rotate)
                                Op0CA = (int16) DSP1.parameters [0];
                                Op0CX1 = (int16) DSP1.parameters [1];
                                Op0CY1 = (int16) DSP1.parameters [2];

                                DSPOp0C ();

                                DSP1.out_count = 2;
                                DSP1.output [0] = (uint16) Op0CX2;
                                DSP1.output [1] = (uint16) Op0CY2;
                                break;

                            case 0x1c:  // Polar (3D rotate)
                                Op1CZ = DSP1.parameters [0];
                                Op1CX = DSP1.parameters [1];
                                Op1CY = DSP1.parameters [2];
                                Op1CXBR = DSP1.parameters [3];
                                Op1CYBR = DSP1.parameters [4];
                                Op1CZBR = DSP1.parameters [5];

                                DSPOp1C ();

                                DSP1.out_count = 3;
                                DSP1.output [0] = (uint16) Op1CXAR;
                                DSP1.output [1] = (uint16) Op1CYAR;
                                DSP1.output [2] = (uint16) Op1CZAR;
                                break;

                            case 0x02:  // Parameter (Projection)
                                Op02FX = DSP1.parameters [0];
                                Op02FY = DSP1.parameters [1];
                                Op02FZ = DSP1.parameters [2];
                                Op02LFE = DSP1.parameters [3];
                                Op02LES = DSP1.parameters [4];
                                Op02AAS = DSP1.parameters [5];
                                Op02AZS = DSP1.parameters [6];

                                DSPOp02 ();

                                DSP1.out_count = 4;
                                DSP1.output [0] = Op02VOF;
                                DSP1.output [1] = Op02VVA;
                                DSP1.output [2] = Op02CX;
                                DSP1.output [3] = Op02CY;
                                break;

                            case 0x1a:  // Raster mode 7 matrix data
                            case 0x0a:
                                Op0AVS = DSP1.parameters [0];

                                DSPOp0A ();

                                DSP1.out_count = 4;
                                DSP1.output [0] = Op0AA;
                                DSP1.output [1] = Op0AB;
                                DSP1.output [2] = Op0AC;
                                DSP1.output [3] = Op0AD;
                                break;

                            case 0x06:  // Project object
                                Op06X = (int16) DSP1.parameters [0];
                                Op06Y = (int16) DSP1.parameters [1];
                                Op06Z = (int16) DSP1.parameters [2];

                                DSPOp06 ();

                                DSP1.out_count = 3;
                                DSP1.output [0] = Op06H;
                                DSP1.output [1] = Op06V;
                                DSP1.output [2] = Op06S;
                                break;
                            
                            case 0x0e:  // Target
                                Op0EH = (int16) DSP1.parameters [0];
                                Op0EV = (int16) DSP1.parameters [1];

                                DSPOp0E ();

                                DSP1.out_count = 2;
                                DSP1.output [0] = Op0EX;
                                DSP1.output [1] = Op0EY;
                                break;

                            // Extra commands used by Pilot Wings
                            case 0x01: // Set attitude matrix A
                                Op01m = (int16) DSP1.parameters [0];
                                Op01Zr = (int16) DSP1.parameters [1];
                                Op01Xr = (int16) DSP1.parameters [2];
                                Op01Yr = (int16) DSP1.parameters [3];

                                DSPOp01 ();
                                break;

                            case 0x11:  // Set attitude matrix B
                                Op11m = (int16) DSP1.parameters [0];
                                Op11Zr = (int16) DSP1.parameters [1];
                                Op11Xr = (int16) DSP1.parameters [2];
                                Op11Yr = (int16) DSP1.parameters [3];

                                DSPOp11 ();
                                break;

                            case 0x21:  // Set attitude matrix C
                                Op21m = (int16) DSP1.parameters [0];
                                Op21Zr = (int16) DSP1.parameters [1];
                                Op21Xr = (int16) DSP1.parameters [2];
                                Op21Yr = (int16) DSP1.parameters [3];

                                DSPOp21 ();
                                break;

                            case 0x0d:  // Objective matrix A
                                Op0DX = (int16) DSP1.parameters [0];
                                Op0DY = (int16) DSP1.parameters [1];
                                Op0DZ = (int16) DSP1.parameters [2];

                                DSPOp0D ();

                                DSP1.out_count = 3;
                                DSP1.output [0] = (uint16) Op0DF;
                                DSP1.output [1] = (uint16) Op0DL;
                                DSP1.output [2] = (uint16) Op0DU;
                                break;

                            case 0x1d:  // Objective matrix B
                                Op1DX = (int16) DSP1.parameters [0];
                                Op1DY = (int16) DSP1.parameters [1];
                                Op1DZ = (int16) DSP1.parameters [2];

                                DSPOp1D ();

                                DSP1.out_count = 3;
                                DSP1.output [0] = (uint16) Op1DF;
                                DSP1.output [1] = (uint16) Op1DL;
                                DSP1.output [2] = (uint16) Op1DU;
                                break;

                            case 0x2d:  // Objective matrix C
                                Op2DX = (int16) DSP1.parameters [0];
                                Op2DY = (int16) DSP1.parameters [1];
                                Op2DZ = (int16) DSP1.parameters [2];

                                DSPOp2D ();

                                DSP1.out_count = 3;
                                DSP1.output [0] = (uint16) Op2DF;
                                DSP1.output [1] = (uint16) Op2DL;
                                DSP1.output [2] = (uint16) Op2DU;
                                break;

                            case 0x03:  // Subjective matrix A
                                Op03F = (int16) DSP1.parameters [0];
                                Op03L = (int16) DSP1.parameters [1];
                                Op03U = (int16) DSP1.parameters [2];

                                DSPOp03 ();

                                DSP1.out_count = 3;
                                DSP1.output [0] = (uint16) Op03X;
                                DSP1.output [1] = (uint16) Op03Y;
                                DSP1.output [2] = (uint16) Op03Z;
                                break;

                            case 0x13:  // Subjective matrix B
                                Op13F = (int16) DSP1.parameters [0];
                                Op13L = (int16) DSP1.parameters [1];
                                Op13U = (int16) DSP1.parameters [2];

                                DSPOp13 ();

                                DSP1.out_count = 3;
                                DSP1.output [0] = (uint16) Op13X;
                                DSP1.output [1] = (uint16) Op13Y;
                                DSP1.output [2] = (uint16) Op13Z;
                                break;

                            case 0x23:  // Subjective matrix C
                                Op23F = (int16) DSP1.parameters [0];
                                Op23L = (int16) DSP1.parameters [1];
                                Op23U = (int16) DSP1.parameters [2];

                                DSPOp23 ();

                                DSP1.out_count = 3;
                                DSP1.output [0] = (uint16) Op23X;
                                DSP1.output [1] = (uint16) Op23Y;
                                DSP1.output [2] = (uint16) Op23Z;
                                break;

                            case 0x0b:
                                Op0BX = (int16) DSP1.parameters [0];
                                Op0BY = (int16) DSP1.parameters [1];
                                Op0BZ = (int16) DSP1.parameters [2];

                                DSPOp0B ();

                                DSP1.out_count = 1;
                                DSP1.output [0] = (uint16) Op0BS;
                                break;

                            case 0x1b:
                                Op1BX = (int16) DSP1.parameters [0];
                                Op1BY = (int16) DSP1.parameters [1];
                                Op1BZ = (int16) DSP1.parameters [2];

                                DSPOp1B ();

                                DSP1.out_count = 1;
                                DSP1.output [0] = (uint16) Op1BS;
                                break;
                                
                            case 0x2b:
                                Op2BX = (int16) DSP1.parameters [0];
                                Op2BY = (int16) DSP1.parameters [1];
                                Op2BZ = (int16) DSP1.parameters [2];

                                DSPOp0B ();

                                DSP1.out_count = 1;
                                DSP1.output [0] = (uint16) Op2BS;
                                break;

                            case 0x14:  // Gyrate
                                Op14Zr = (int16) DSP1.parameters [0];
                                Op14Xr = (int16) DSP1.parameters [1];
                                Op14Yr = (int16) DSP1.parameters [2];
                                Op14U = (int16) DSP1.parameters [3];
                                Op14F = (int16) DSP1.parameters [4];
                                Op14L = (int16) DSP1.parameters [5];

                                DSPOp14 ();

                                DSP1.out_count = 3;
                                DSP1.output [0] = (uint16) Op14Zrr;
                                DSP1.output [1] = (uint16) Op14Xrr;
                                DSP1.output [2] = (uint16) Op14Yrr;
                                break;

                                                case 0x0F:
                                                        Op0FRamsize = (int16) DSP1.parameters [0];

                                                        DSPOp0F ();

                                                        DSP1.out_count = 1;
                                                        DSP1.output [0] = (uint16) Op0FPass;
                                                        break;

                            default:
                                break;
                        }
                    }
                                        else DSP1.in_index++;
                }
            }
        }
    }
}